[Swift] CIFaceFeatureで顔を検出してマスクをつけてみた
はじめに
iOS7から追加されたCIFaceFeatureですが、画像の中の人の顔を見つけ出し、顔に関する情報を取得する事が出来ます。
今回はタイトルの通り、CIFaceFeatureで写真の中の人の顔を検出して上からマスク画像をかぶせてみました。
実装
画面構成
プロジェクトはSingleViewApplicationで作成します。 そして、storyboardは下図のように配置しました。
ViewController.swift に UIImageViewのアウトレットを設定します。
また、ボタンのアクションも登録します。
素材の準備
今回は[email protected]という名前の透過PNGを用意しました。
Images.xcassetsを開いて画像をドラックすればOKです。
ソースコード
import UIKit class ViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate { /** 写真表示用ImageView */ @IBOutlet weak var photoImageView: UIImageView! /** 画像取得用 */ var picker:UIImagePickerController? /** 画像認識 */ var detector:CIDetector? override func viewDidLoad() { super.viewDidLoad() detector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: [CIDetectorAccuracy:CIDetectorAccuracyHigh]) picker = UIImagePickerController() picker!.delegate = self picker!.allowsEditing = false messageTextView.resignFirstResponder() } /** * アルバムボタンをタップ */ @IBAction func onTapAlbumButton(sender: AnyObject) { //ライブラリが使用できるかどうか判定 if(UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.PhotoLibrary)) { self.presentViewController(picker!, animated: true, completion: nil) } } /** * 写真選択 */ func imagePickerController(picker: UIImagePickerController!, didFinishPickingImage image: UIImage!, editingInfo: NSDictionary!){ self.dismissViewControllerAnimated(true, completion: nil) //画像を変換 var ciImage:CIImage = CIImage(image: image) // グラフィックスコンテキスト生成 UIGraphicsBeginImageContextWithOptions(image.size, true, 0); // 読み込んだ写真を書き出す image.drawInRect(CGRectMake(0, 0, image.size.width, image.size.height)) // 取得するパラメーターを指定する let options = [CIDetectorSmile : true, CIDetectorEyeBlink : true] // 画像内に顔があるか調べる let features = detector?.featuresInImage(ciImage, options: options) for feature in features as [CIFaceFeature] { // マスク画像 let uiImage = UIImage(named:"mask") var faceRect = feature.bounds // 位置の補正 faceRect.origin.y = image.size.height - faceRect.origin.y - faceRect.size.height // 顔の場所に書き出す uiImage?.drawInRect(faceRect) } // グラフィックスコンテキストの画像を取得 var outputImage = UIGraphicsGetImageFromCurrentImageContext() // グラフィックスコンテキストの編集を終了 UIGraphicsEndImageContext(); // 画像を出力 photoImageView.image = outputImage } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } }
以下のループ内で顔があるか判定し、顔のサイズ/位置を取得してマスク画像を出力してます。 (CoreImageの座標系は左下が(0,0)になるため、位置の補正を行っています)
let features = detector?.featuresInImage(ciImage, options: options) for feature in features as [CIFaceFeature] { // マスク画像 let uiImage = UIImage(named:"mask") var faceRect = feature.bounds // 位置の補正 faceRect.origin.y = image.size.height - faceRect.origin.y - faceRect.size.height // 顔の場所に書き出す uiImage?.drawInRect(faceRect) }
今回は位置情報(bounds)しか使ってませんが、CIFaceFeatureでは以下の情報が取れる様です。笑顔とか取得できるのが興味深いです。
プロパティ | 型 | 説明 |
---|---|---|
bounds | CGRect | 顔の大きさ/位置情報 |
faceAngle | float | 顔の傾き |
leftEyePosition | CGPoint | 左目の位置 |
rightEyePosition | CGPoint | 右目の位置 |
mouthPosition | CGPoint | 口の位置 |
hasSmile | BOOL | 笑顔かどうか |
leftEyeClosed | BOOL | 左目が閉じているかどうか |
rightEyeClosed | BOOL | 右目が閉じているかどうか |
( CIFaceFeature Class Reference )
実行結果について
人が写っている写真で試してみました。
顔を判定してマスク画像がついたのですが、 結果を見るまで頭全体が取れると勘違いしてました。。。顔面エリア(?)が取れる感じですね。
袋を被っている感じにしたかった。。。顔面なら、お面っぽい画像にすればよかった・・・(´・ω・`)
最後に
CIFaceFeatureですが、最初は位置情報くらいしか取得できないと思っていました。想像以上に色々なプロパティがあって楽しかったです。
また精度について個人的な所感を。 試してみたところ、顔が認識されやすい人とされにくい人が居る印象を受けました。また横顔は認識されず、斜め顏は認識される時とされない時がありました。